# -*- coding: utf-8 -*-

"""一个简单的四则运算计算器
小试牛刀，主要目的为理解语法解析中的递归下降算法。
1）语法是规则，如同我们要表达的事物；文法是表达的方式，可以口述，书写，比划等。一般用书写的方式表达语法叫文法。
2）语法解析的主要任务是写出响应的上下文无关文法。
3）文法的书写过程是将规则（表达式）一步步推导成子规则（子表达式）与Token的过程，直至只有Token。
4）语法解析程序基本上跟嵌套文法规则一致。上级文法嵌套下级文法，上级算法调用下级算法。表现在生成ast中，上级算法生成上级节点，下级算法生成下级节点，下级节点成为相应子节点。这就是下降算法的本质。
5）递归，主要表现在算术表达式等连续表达式上，为了达到解析完成的目的，如加法规则文法结构可以写成：加法规则的子规则也可以是加法规则本身。
"""

from simple_enum import ASTNodeType, TokenType
from simple_parser import SimpleASTNode, TokenReader
from simple_lexer import Token, FiniteAuto


class SimpleParser():
    """
    Program: ExpressionStmt+
    ExpressionStmt: IntDeclaration | AssignmentExpressionStmt | AdditiveExpressionStmt

    IntDeclaration: Int Identifier Semicolon | Int AssignmentExpression Semicolon
    AssignmentExpression: Identifier Assignment AdditiveExpression

    # 左递归
    # AdditiveExpression: MultiplicativeExpression ^Plus | AdditiveExpression Plus MultiplicativeExpression ^Plus
    # MultiplicativeExpression: Primary ^Star | MultiplicativeExpression Star Primary ^Star
    
    AdditiveExpression: MultiplicativeExpression ^Plus | MultiplicativeExpression Plus AdditiveExpression ^Plus
    MultiplicativeExpression: Primary ^Star | Primary Star MultiplicativeExpression ^Star
    
    Primary: Identifier | IntLiteral | LeftParen AdditiveExpression RightParen
    
    AssignmentExpressionStmt: AssignmentExpression Semicolon

    AdditiveExpressionStmt: AdditiveExpression Semicolon
    """
    def __init__(self, tokens) -> None:
        # 引入哨兵Token，简便处理流程
        tokens.append(Token(token_type=TokenType.Semicolon))
        self.tokenReader = TokenReader(tokens)
    
    def parse_program(self):
        root = SimpleASTNode(ASTNodeType.Program, text='root')
        while True:
            if self.tokenReader.peek().token_type is TokenType.Semicolon:
                break
            node = self.parse_expressionStmt()
            if node:
                root.add_child(node)
            else:
                print('Illegal Grammer')
                break
        return root

    def parse_expressionStmt(self):
        # 回溯点
        pos = self.tokenReader.get_position()
        node = self.parse_intDeclaration()
        if node:
            return node
        
        # 回溯
        self.tokenReader.set_position(pos)

        node = self.parse_assignmentExpressionStmt()
        if node:
            return node

        # 回溯
        self.tokenReader.set_position(pos)

        node = self.parse_additiveExpressionStmt()
        if node:
            return node

        # 回溯
        self.tokenReader.set_position(pos)

    def parse_intDeclaration(self):
        pos = self.tokenReader.get_position()
        if self.tokenReader.peek().token_type is TokenType.Int:
            token_int = self.tokenReader.read()
            if self.tokenReader.peek().token_type is TokenType.Identifier:
                token_identifier = self.tokenReader.read()
                if self.tokenReader.peek().token_type is TokenType.Semicolon:
                    self.tokenReader.read()
                    node = SimpleASTNode(ASTNodeType.IntDeclaration, text=token_int.text)
                    child = SimpleASTNode(ASTNodeType.Identifier, text=token_identifier.text)
                    node.add_child(child)
                    return node

        # 回溯
        self.tokenReader.set_position(pos)
        if self.tokenReader.peek().token_type is TokenType.Int:
            token_int = self.tokenReader.read()
            child = self.parse_assignmentExpression()
            if child:
                if self.tokenReader.peek().token_type is TokenType.Semicolon:
                    self.tokenReader.read()
                    node = SimpleASTNode(ASTNodeType.IntDeclaration, text=token_int.text)
                    node.add_child(child)
                    return node
        
        self.tokenReader.set_position(pos)

    def parse_assignmentExpressionStmt(self):
        pos = self.tokenReader.get_position()

        child = self.parse_assignmentExpression()
        if child:
            if self.tokenReader.peek().token_type is TokenType.Semicolon:
                self.tokenReader.read()
                return child

        self.tokenReader.set_position(pos)
    
    def parse_additiveExpressionStmt(self):
        pos = self.tokenReader.get_position()

        child = self.parse_additiveExpression()
        if child:
            if self.tokenReader.peek().token_type is TokenType.Semicolon:
                self.tokenReader.read()
                return child
        
        self.tokenReader.set_position(pos)

    def parse_assignmentExpression(self):
        pos = self.tokenReader.get_position()

        if self.tokenReader.peek().token_type is TokenType.Identifier:
            token_id = self.tokenReader.read()
            if self.tokenReader.peek().token_type is TokenType.Assignment:
                token_assign = self.tokenReader.read()
                child = self.parse_additiveExpression()
                if child:
                    node = SimpleASTNode(ASTNodeType.AssignmentExpression, text=token_assign.text)
                    child_id = SimpleASTNode(ASTNodeType.Identifier, text=token_id.text)
                    node.add_child(child_id)
                    node.add_child(child)
                    return node
        
        self.tokenReader.set_position(pos)

    # def parse_additiveExpression(self):
    #     """按照最初上下文无关文法格式的过程实现，对 + 操作会出现左递归"""
    #     pos = self.tokenReader.get_position()

    #     child = self.parse_multiplicativeExpression()
    #     if child:
    #         if self.tokenReader.peek().token_type is not TokenType.Plus:
    #             # node = SimpleASTNode(ASTNodeType.AdditiveExpression, text='Literal')
    #             # node.add_child(child)
    #             return child
        
    #     self.tokenReader.set_position(pos)
    #     node_additive = self.parse_additiveExpression()
    #     if node_additive:
    #         if self.tokenReader.peek().token_type is TokenType.Plus:
    #             token_plus = self.tokenReader.read()
    #             node_multiplicative = self.parse_multiplicativeExpression()
    #             if node_multiplicative:
    #                 if self.tokenReader.peek().token_type is not TokenType.Plus:
    #                     node = SimpleASTNode(ASTNodeType.AdditiveExpression, text=token_plus.text)
    #                     node.add_child(node_additive)
    #                     node.add_child(node_multiplicative)
    #                     return node

    #     self.tokenReader.set_position(pos)
    
    def parse_additiveExpression(self):
        """优化过的上下文无关文法格式的过程实现，对 + 操作不会出现左递归"""
        pos = self.tokenReader.get_position()

        node_multi = self.parse_multiplicativeExpression()
        if node_multi:
            if self.tokenReader.peek().token_type is not TokenType.Plus:
                # node = SimpleASTNode(ASTNodeType.AdditiveExpression, text='Literal')
                # node.add_child(child)
                return node_multi
        
        if node_multi:
            if self.tokenReader.peek().token_type is TokenType.Plus:
                token_plus = self.tokenReader.read()
                node_additive = self.parse_additiveExpression()
                if node_additive:
                    if self.tokenReader.peek().token_type is not TokenType.Plus:
                        node = SimpleASTNode(ASTNodeType.AdditiveExpression, token_plus.text)
                        node.add_child(node_multi)
                        node.add_child(node_additive)
                        return node

        self.tokenReader.set_position(pos)
    
    # def parse_multiplicativeExpression(self):
    #     """按照最初上下文无关文法格式的过程实现，对 * 操作会出现左递归"""
    #     pos = self.tokenReader.get_position()

    #     child = self.parse_primary()
    #     if child:
    #         if self.tokenReader.peek().token_type is not TokenType.Star:
    #             # node = SimpleASTNode(ASTNodeType.MultiplicativeExpression, text='Literal')
    #             # node.add_child(child)
    #             return child
        
    #     self.tokenReader.set_position(pos)
    #     node_multi = self.parse_multiplicativeExpression()
    #     if node_multi:
    #         if self.tokenReader.peek().token_type is TokenType.Star:
    #             token_star = self.tokenReader.read()
    #             node_primary = self.parse_primary()
    #             if node_primary:
    #                 if self.tokenReader.peek().token_type is not TokenType.Star:
    #                     node = SimpleASTNode(ASTNodeType.MultiplicativeExpression, token_star.text)
    #                     node.add_child(node_multi)
    #                     node.add_child(node_primary)
    #                     return node

    #     self.tokenReader.set_position(pos)
    
    def parse_multiplicativeExpression(self):
        """优化过的上下文无关文法格式的过程实现，对 * 操作不会出现左递归"""
        pos = self.tokenReader.get_position()

        node_primary = self.parse_primary()
        if node_primary:
            if self.tokenReader.peek().token_type is not TokenType.Star:
                # node = SimpleASTNode(ASTNodeType.MultiplicativeExpression, text='Literal')
                # node.add_child(child)
                return node_primary
        
        if node_primary:
            if self.tokenReader.peek().token_type is TokenType.Star:
                token_star = self.tokenReader.read()
                node_multi = self.parse_multiplicativeExpression()
                if node_multi:
                    if self.tokenReader.peek().token_type is not TokenType.Star:
                        node = SimpleASTNode(ASTNodeType.MultiplicativeExpression, token_star.text)
                        node.add_child(node_primary)
                        node.add_child(node_multi)
                        return node

        self.tokenReader.set_position(pos)
    
    def parse_primary(self):
        pos = self.tokenReader.get_position()

        if self.tokenReader.peek().token_type is TokenType.Identifier:
            token_id = self.tokenReader.read()
            return SimpleASTNode(ASTNodeType.Identifier, token_id.text)

        self.tokenReader.set_position(pos)
        if self.tokenReader.peek().token_type is TokenType.IntLiteral:
            token_intLiteral = self.tokenReader.read()
            return SimpleASTNode(ASTNodeType.IntLiteral, token_intLiteral.text)
        
        self.tokenReader.set_position(pos)
        if self.tokenReader.peek().token_type is TokenType.LeftParen:
            token_leftParen = self.tokenReader.read()
            node_additive = self.parse_additiveExpression()
            if node_additive:
                if self.tokenReader.peek().token_type is TokenType.RightParen:
                    token_rightParen = self.tokenReader.read()
                    node_paren = SimpleASTNode(ASTNodeType.Paren, text='%s %s' % (token_leftParen.text, token_rightParen.text))
                    node_paren.add_child(node_additive)
                    return node_paren

        self.tokenReader.set_position(pos)


def caculate(ast):
    expre = ast.text
    results = []
    
    if not ast.children:
        results.append(ast.text)

    for child in ast.children:
        results.append(caculate(child))

    if len(results) == 1:
        return results[0]
    elif len(results) == 2:
        return eval('%s%s%s' % (results[0], expre, results[1]))
print(FiniteAuto() is FiniteAuto())

def test(text):
    
    finiteAuto = FiniteAuto()
    finiteAuto.prepare_container([])
    finiteAuto.tokenize(text)
    tokens = finiteAuto.dump_tokens()
    # print(tokens)
    
    for token in tokens:
        print('%s    %s' % (token.token_type, token.text))

    print('\n')

    parser = SimpleParser(tokens)
    ast = parser.parse_program()
    # for node in ast:
    #     print(node.text)

    ast.show_ast_pretty()

    result = caculate(ast)
    
    print('< %s >的计算结果为[ %s ]' % (text, result))

if __name__ == '__main__':
    test('2+3+7;')
    test('2+3*8;')
    test ('2+3*8+1*9+2+3+3*2;')
    
